﻿using System;
using System.Collections.Generic;
using Fcdbas.Core.Exceptions;

namespace Fcdbas.Core.Common
{
    /// <summary>身份证</summary>
    public class IdentityCard
    {
        #region 属性
        private DateTime _Birthday;
        /// <summary>生日</summary>
        public DateTime Birthday
        {
            get { return _Birthday; }
            set { _Birthday = value; }
        }

        /// <summary>性别</summary>
        public enum SexType
        {
            /// <summary>男</summary>
            Man,

            /// <summary>女</summary>
            Woman
        }

        private SexType _Sex;
        /// <summary>性别</summary>
        public SexType Sex
        {
            get { return _Sex; }
            set { _Sex = value; }
        }

        private Boolean _IsOld;
        /// <summary>是否15位旧卡</summary>
        public Boolean IsOld
        {
            get { return _IsOld; }
            set { _IsOld = value; }
        }

        private String _AreaNum;
        /// <summary>地区编码</summary>
        public String AreaNum
        {
            get { return _AreaNum; }
            set { _AreaNum = value; }
        }
        #endregion

        #region 构造函数
        /// <summary>初始化</summary>
        public IdentityCard() { }
        #endregion

        #region 验证
        /// <summary>验证身份证是否合法</summary>
        /// <param name="card"></param>
        /// <returns></returns>
        public static Boolean Valid(String card)
        {
            try
            {
                if (Parse(card) != null) return true;
            }
            catch { }

            return false;
        }
        #endregion

        #region 分析
        private static Dictionary<String, IdentityCard> cache = new Dictionary<String, IdentityCard>();

        /// <summary>使用身份证号码初始化</summary>
        /// <param name="card"></param>
        /// <returns></returns>
        public static IdentityCard Parse(String card)
        {
            if (String.IsNullOrEmpty(card)) return null;
            if (card.Length != 15 && card.Length != 18) return null;

            //转为小写，18位身份证后面有个字母x
            card = card.ToLower();

            IdentityCard ic = null;
            if (cache.TryGetValue(card, out ic)) return ic;
            lock (cache)
            {
                if (cache.TryGetValue(card, out ic)) return ic;

                IdentityCard idc = Parse2(card);
                cache.Add(card, idc);
                return idc;
            }
        }

        private static IdentityCard Parse2(String card)
        {
            String area = card.Substring(0, 6);

            IdentityCard idc = new IdentityCard();
            idc.AreaNum = ParseArea(area);

            if (card.Length == 15)
                idc.ParseBirthdayAndSex15(card);
            else if (card.Length == 18)
            {
                idc.ParseBirthdayAndSex18(card);

                //校验码验证  GB11643-1999标准
                String[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(',');
                Int32[] Wi = new Int32[] { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
                char[] Ai = card.Remove(17).ToCharArray();
                int sum = 0;
                for (int i = 0; i < 17; i++)
                {
                    sum += Wi[i] * (Ai[i] - '0');
                }

                int y = -1;
                Math.DivRem(sum, 11, out y);
                if (arrVarifyCode[y] != card.Substring(17, 1).ToLower())
                    throw new XException("验证码校验失败！"); 
            }

            return idc;
        }
        #endregion

        #region 分析地区
        private static List<Int32> ads = new List<Int32>(new Int32[] { 11, 22, 35, 44, 53, 12, 23, 36, 45, 54, 13, 31, 37, 46, 61, 14, 32, 41, 50, 62, 15, 33, 42, 51, 63, 21, 34, 43, 52, 64, 65, 71, 81, 82, 91 });

        private static String ParseArea(String area)
        {
            Int32 n = 0;
            if (!Int32.TryParse(area, out n)) throw new XException("非法地区编码！");

            String str = area.Substring(0, 2);
            if (!Int32.TryParse(str, out n)) throw new XException("非法省份编码！");

            if (!ads.Contains(n)) throw new XException("没有找到该省份！");

            return area;
        }
        #endregion

        #region 分析生日、性别
        private void ParseBirthdayAndSex15(String card)
        {
            String str = card.Substring(6, 2);
            Int32 n = Int32.Parse(str);

            if (n < 20)
                n = 20;
            else
                n = 19;

            String birth = n.ToString() + card.Substring(6, 6).Insert(2, "-").Insert(5, "-");
            DateTime d = DateTime.MinValue;
            if (!DateTime.TryParse(birth, out d)) throw new XException("生日不正确！");
            Birthday = d;

            //最后一位是性别
            n = Convert.ToInt32(card.Substring(card.Length - 1, 1));
            Boolean man = n % 2 != 0;

            if (man)
                Sex = SexType.Man;
            else
                Sex = SexType.Woman;
        }

        private void ParseBirthdayAndSex18(String card)
        {
            String birth = card.Substring(6, 8).Insert(4, "-").Insert(7, "-");
            DateTime d = DateTime.MinValue;
            if (!DateTime.TryParse(birth, out d)) throw new XException("生日不正确！");
            Birthday = d;

            //倒数第二位是性别
            Int32 n = Convert.ToInt32(card.Substring(card.Length - 2, 1));
            Boolean man = n % 2 != 0;

            if (man)
                Sex = SexType.Man;
            else
                Sex = SexType.Woman;
        }
        #endregion
    }
}